package cinvestav.adviseme.databasemodel;

import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import cinvestav.adviseme.datamodel.Persona;
import cinvestav.adviseme.datamodel.Solicitud;
import cinvestav.adviseme.util.DBException;
import cinvestav.adviseme.util.DataBaseDictionary;
import cinvestav.adviseme.webservice.SolicitudWebService;

public class SolicitudDB extends DataBase implements ISolicitudDB{

	/**
	 * Constructor vacio
	 * @throws DBException
	 */
	public SolicitudDB() throws DBException {
		super();
		// TODO Auto-generated constructor stub
	}

	@Override
	public List<Solicitud> getAll() throws DBException {
		// TODO Auto-generated method stub
		try {
			Connection c = this.getConnection();
			ResultSet res = this.ejecutarQuery(c.prepareStatement(DataBaseDictionary.GET_ALL_SOLICITUDES));
			List<Solicitud> solicitudes = new LinkedList<Solicitud>();
			while(res.next()){
				Solicitud solicitud = new Solicitud();
				Persona personaSolicitante = new Persona();
				Persona personaSolicitada = new Persona();
				solicitud.setId(res.getInt("ide_sol"));				
				solicitud.setEstado(res.getInt("res_sol"));			
				
				personaSolicitante.setId(res.getInt("p1.ide_per"));
				personaSolicitante.setNombreUsuario(res.getString("p1.usu_per"));
				personaSolicitante.setNombrePersona(res.getString("p1.nom_per"));
				personaSolicitante.setApellidoPaterno(res.getString("p1.app_per"));
				personaSolicitante.setApellidoMaterno(res.getString("p1.apm_per"));
				personaSolicitante.setCorreo(res.getString("p1.cor_per"));
				personaSolicitante.setSexo(res.getBoolean("p1.sex_per"));
				personaSolicitante.setFotografia(res.getString("p1.fot_per"));
				
				personaSolicitada.setId(res.getInt("p2.ide_per"));				
				personaSolicitada.setNombreUsuario(res.getString("p2.usu_per"));
				personaSolicitada.setNombrePersona(res.getString("p2.nom_per"));
				personaSolicitada.setApellidoPaterno(res.getString("p2.app_per"));
				personaSolicitada.setApellidoMaterno(res.getString("p2.apm_per"));
				personaSolicitada.setCorreo(res.getString("p2.cor_per"));
				personaSolicitada.setSexo(res.getBoolean("p2.sex_per"));
				personaSolicitada.setFotografia(res.getString("p2.fot_per"));
				
				solicitud.setSolicitante(personaSolicitante);
				solicitud.setSolicitado(personaSolicitada);
				
				solicitudes.add(solicitud);
			}
			c.close();
			return solicitudes;
		} catch (SQLException e) {
			// TODO: handle exception
			throw new DBException("Error al ejecutar la consulta:\n"+
					DataBaseDictionary.GET_ALL_SOLICITUDES);
		}
	}

	@Override
	public List<Solicitud> getByFiter(Map<String, String[]> filtros)
			throws DBException {
		try {
            //Se inicializa la coneccion
            //Se obtiene una nueva conexion
            Connection c = this.getConnection();
            //Se realiza la consulta
            ResultSet res = this.ejecutarQuery(genPreparedStatementString(filtros, c));
            //Procesamiento de resultados
            List<Solicitud> solicitudes= new LinkedList<Solicitud>();
            //Por cada elemento en el resultSet, se genera una nueva linea y se
            //agrega a la lista
            while (res.next()) {
                //Los elementos del resultSet se obtienen a partir del nombre de la columna
                //O por su indice en la tabla
                Solicitud solicitud= new Solicitud();
                Persona personaSolicitante = new Persona();
				Persona personaSolicitada = new Persona();
				solicitud.setId(res.getInt("ide_sol"));				
				solicitud.setEstado(res.getInt("res_sol"));		
				Date aux = res.getDate("fec_sol");
				if (aux!= null){
					solicitud.setFechaSolicitud(res.getDate("fec_sol").toString());
				}else{
					solicitud.setFechaSolicitud("");
				}
				aux = res.getDate("fre_sol");
				if (aux!= null){
					solicitud.setFechaRespuesta(res.getDate("fre_sol").toString());
				}else{
					solicitud.setFechaRespuesta("");
				}
			
				
				personaSolicitante.setId(res.getInt(DataBaseDictionary.ALIAS_PERSONA_SOLICITANTE+".ide_per"));
				personaSolicitante.setNombreUsuario(res.getString(DataBaseDictionary.ALIAS_PERSONA_SOLICITANTE+".usu_per"));
				personaSolicitante.setNombrePersona(res.getString(DataBaseDictionary.ALIAS_PERSONA_SOLICITANTE+".nom_per"));
				personaSolicitante.setApellidoPaterno(res.getString(DataBaseDictionary.ALIAS_PERSONA_SOLICITANTE+".app_per"));
				personaSolicitante.setApellidoMaterno(res.getString(DataBaseDictionary.ALIAS_PERSONA_SOLICITANTE+".apm_per"));
				personaSolicitante.setCorreo(res.getString(DataBaseDictionary.ALIAS_PERSONA_SOLICITANTE+".cor_per"));
				personaSolicitante.setSexo(res.getBoolean(DataBaseDictionary.ALIAS_PERSONA_SOLICITANTE+".sex_per"));
				personaSolicitante.setFotografia(res.getString(DataBaseDictionary.ALIAS_PERSONA_SOLICITANTE+".fot_per"));
				
				personaSolicitada.setId(res.getInt(DataBaseDictionary.ALIAS_PERSONA_SOLICITADO+".ide_per"));				
				personaSolicitada.setNombreUsuario(res.getString(DataBaseDictionary.ALIAS_PERSONA_SOLICITADO+".usu_per"));
				personaSolicitada.setNombrePersona(res.getString(DataBaseDictionary.ALIAS_PERSONA_SOLICITADO+".nom_per"));
				personaSolicitada.setApellidoPaterno(res.getString(DataBaseDictionary.ALIAS_PERSONA_SOLICITADO+".app_per"));
				personaSolicitada.setApellidoMaterno(res.getString(DataBaseDictionary.ALIAS_PERSONA_SOLICITADO+".apm_per"));
				personaSolicitada.setCorreo(res.getString(DataBaseDictionary.ALIAS_PERSONA_SOLICITADO+".cor_per"));
				personaSolicitada.setSexo(res.getBoolean(DataBaseDictionary.ALIAS_PERSONA_SOLICITADO+".sex_per"));
				personaSolicitada.setFotografia(res.getString(DataBaseDictionary.ALIAS_PERSONA_SOLICITADO+".fot_per"));
				
				solicitud.setSolicitante(personaSolicitante);
				solicitud.setSolicitado(personaSolicitada);

                solicitudes.add(solicitud);
            }
            c.close();
            return solicitudes;
        } catch (SQLException ex) {
            Logger.getLogger(PersonaDB.class.getName()).log(Level.SEVERE, null, ex);
            throw new DBException("Error al ejecutar la consulta:\n" + ex.getMessage());
        } catch (DBException ex) {
            throw new DBException(ex);
        }
	}

	@Override
	public Boolean delete(Solicitud persona) throws DBException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Boolean insert(Solicitud solicitud) throws DBException {
		// TODO Auto-generated method stub
		try {
			
            //Se inicializa la coneccion
            //Se obtiene una nueva conexion
            Connection c = this.getConnection();
            //Se verifica que la solicitud no haya sido creada previamente o no exista una relacion de amistad previa
            PreparedStatement psValidacion = c.prepareStatement(DataBaseDictionary.CHECK_SOLICITUD_AMISTAD_EXISTENTE);
            psValidacion.setInt(1,solicitud.getSolicitado().getId());
            psValidacion.setInt(2,solicitud.getSolicitante().getId());
            
            psValidacion.setInt(3,solicitud.getSolicitado().getId());
            psValidacion.setInt(4,solicitud.getSolicitante().getId());
            
            psValidacion.setInt(5,solicitud.getSolicitado().getId());
            psValidacion.setInt(6,solicitud.getSolicitante().getId());
            
            psValidacion.setInt(7,solicitud.getSolicitado().getId());
            psValidacion.setInt(8,solicitud.getSolicitante().getId());
            ResultSet res = this.ejecutarQuery(psValidacion);
            int cont = 0;
            //Si existe algun resultado en la consulta, quiere decir que existe una solicitud previa o una relacion de amistar
            //entre el solicitante y el solicitado
            while(res.next()){
            	cont++;
            }
            //Si hay mas de un resultado se crea una exception
            if (cont>0) throw new DBException("SOLICITUD GENERADA PREVIAMENTE");
            
            //Si no, se procede a insertar la solicitud
            PreparedStatement ps = c.prepareStatement(DataBaseDictionary.INSERT_SOLICITUD);
            /*
             * Se insertan los parametros al insert
             *  [1] = Fecha de la Solicitud
             *  [2] = id Solicitante
             *  [3] = id Solicitado
             *  [4] = Estado de la solicitud
             */
            
            ps.setString(1, solicitud.getFechaSolicitud());
            ps.setInt(2, solicitud.getSolicitante().getId());
            ps.setInt(3, solicitud.getSolicitado().getId());
            ps.setInt(4, solicitud.getEstado());

            //Se realiza la consulta
            this.ejecutarInstruccion(ps);
            //Procesamiento de resultados
            c.close();
            return true;
        } catch (SQLException ex) {
            Logger.getLogger(SolicitudDB.class.getName()).log(Level.SEVERE, null, ex);
            throw new DBException("Error al ejecutar la consulta:\n" + ex.getMessage(), ex);
        }
	}
	
	@Override
	public Boolean update(Solicitud solicitud) throws DBException{
		try {
			Connection c = this.getConnection();
			PreparedStatement ps = c.prepareStatement(DataBaseDictionary.UPDATE_SOLICITUD);
			
			ps.setInt(1, solicitud.getEstado());
			ps.setString(2,solicitud.getFechaRespuesta());
			ps.setInt(3, solicitud.getId());
			this.ejecutarInstruccion(ps);
			
			if(solicitud.getEstado() == 2){
				ps = c.prepareStatement(DataBaseDictionary.INSERT_AMIGOSPORPERSONA);				
				ps.setInt(1, solicitud.getSolicitado().getId());				
				ps.setInt(2, solicitud.getSolicitante().getId());				
				this.ejecutarInstruccion(ps);
				
				ps = c.prepareStatement(DataBaseDictionary.INSERT_AMIGOSPORPERSONA);				
				ps.setInt(1, solicitud.getSolicitante().getId());				
				ps.setInt(2, solicitud.getSolicitado().getId());				
				this.ejecutarInstruccion(ps);
				
				
			}
			
			c.close();
			return true;
		} catch (SQLException e) {
			// TODO: handle exception
			Logger.getLogger(Solicitud.class.getName()).log(Level.SEVERE, null, e);
            throw new DBException("Error al ejecutar la consulta:\n" + e.getMessage(), e);
		}		
	}

	@Override
	public List<Solicitud> getPendingRequest(Persona solicitado)
			throws DBException {
		
		/*
         * Mapa para los filtros seleccionados
         *      Key = Filtro a utilizar, definido en la clase "DataBaseDictionary"
         *      Value = Sera un arreglo de dos posiciones donde:
                    [0] = Valor a buscar en la base (Valor del campo)
         *          [1] = Tipo de Dato, deacuerdo a este se realizara el set en el
         *                  PreparedStatement, Los tipos validos estan definidos en "DataBaseDictionary"
         */
		Map<String, String[]> filtros = new HashMap<String, String[]>();
		String[] valor = new String[2];
		
		// Se agrega una entrada al mapa
		valor = new String[3];
		valor[0] = solicitado.getId().toString();
		valor[1] = DataBaseDictionary.COLUMN_TYPE_SOLICITUD_SOLICITADO;
		valor[2] = DataBaseDictionary.ALIAS_SOLICITUD;
		filtros
				.put(DataBaseDictionary.COLUMN_FILTER_NAME_SOLICITUD_SOLICITADO,
						valor);
		
		// Se agrega una entrada al mapa
		valor = new String[3];
		valor[0] = "1";	//Para que se obtengan las solicitudes Pendientes
		valor[1] = DataBaseDictionary.COLUMN_TYPE_SOLICITUD_ESTADO;
		valor[2] = DataBaseDictionary.ALIAS_SOLICITUD;
		filtros
				.put(DataBaseDictionary.COLUMN_FILTER_NAME_SOLICITUD_ESTADO,
						valor);
		
		return getByFiter(filtros);
	}

	@Override
	public List<Solicitud> getNotRespondRequest(Persona solicitante)
			throws DBException {
		// TODO Auto-generated method stub
		return null;
	}
	
	/**
     * Genera un PreparedStatement apartir de un Mapa de filtros y una coneccion
     * @param filtros   Mapa de filtros que se utilizaran para generar la consulta
     * @param c         Conexion que ejecutara el PreparedStatement
     * @return  Un preparedStatement listo para ser ejecutado
     */
    private PreparedStatement genPreparedStatementString(Map<String, String[]> filtros, Connection c) {

    	Boolean ini = Boolean.TRUE;
        //Se obtiene la consulta base
        //A esta cadena se le iran concatenando los filtros segun corresponda
        String statement = DataBaseDictionary.GET_BASE_SOLICITUD;
        //SE obtiene el alias de la tabla a utilizar
        String alias = DataBaseDictionary.ALIAS_SOLICITUD;
        //Se recorre el mapa de filtros para generar el String de la consulta
        for (Map.Entry<String, String[]> entry : filtros.entrySet()) {
            //entry - Contiene la entrada del mapa una por una
            //Si NO es el primer elemento se agrega un AND a la consulla
            if (!ini) {
            	statement += " AND";
            } else {
            //Si es el primer filtro, se agrega la clausula WHERE a la consulta
                ini = Boolean.FALSE;
                statement += " WHERE ";
            }
            //Se agrega el filtro definido por el mapa
            statement += " " + entry.getValue()[2] + "." + entry.getKey();
        }

        //Una vez generada la cadena, se llenaran los datos del PreparedStatement '?'
        try {
            //Se inicializa el PreparedStatement y se liga este a la coneccion
            PreparedStatement ps = c.prepareStatement(statement);
            //llevara el indice de los elementos del ps, debe iniciar en 1
            int cont = 1;
            //Se recorre nuevamente el mapa de filtros
            for (Map.Entry<String, String[]> entry : filtros.entrySet()) {
                String[] val = entry.getValue();
                //Dependiendo del tipo de filtro, se agregan los valores al ps
                if (val[1].compareTo(DataBaseDictionary.STRING_SIMPLE) == 0) {
                    //Si es de tipo 'String_SIMPLE' es decir la busqueda se realiza con '='
                    ps.setString(cont, val[0]);
                } else if (val[1].compareTo(DataBaseDictionary.STRING_JOKER) == 0) {
                    //En este caso la busqueda se realiza con LIKE y se agrega el
                    //comodin de busqueda '%' al final del valor a buscar
                    ps.setString(cont, val[0] + "%");
                } else if (val[1].compareTo(DataBaseDictionary.STRING_DOBLE_JOKER) == 0) {
                    //En este caso la busqueda se realiza con LIKE y se agrega el
                    //comodin de busqueda '%' al final y al inicio del valor a buscar
                    ps.setString(cont, "%" + val[0] + "%");
                } else if (val[1].compareTo(DataBaseDictionary.INTEGER_TYPE) == 0) {
                    //Cuando el valor es entero
                    ps.setInt(cont, Integer.parseInt(val[0]));
                }
                cont++;
            }
            return ps;
        } catch (SQLException ex) {
            Logger.getLogger(PersonaDB.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

}
